package com.leesche.netdoll.communication.protocol.decoder;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.leesche.framework.core.configuration.ConfigurationUtil;
import com.leesche.framework.core.json.GsonUtil;
import com.leesche.framework.core.lang.StringUtil;
import com.leesche.framework.core.lang.UncheckedException;
import com.leesche.netdoll.communication.protocol.SimpleMessage;

/**
 * 解码器管理器(单例)
 * 
 * @author liurongguo
 *
 */
public final class DecoderManager {

	private static final Logger logger = LogManager.getLogger(DecoderManager.class);

	/**
	 * 缓存所有解码器, 见decoder.json配置，key为消息类型，value为消息类型对应的解码器实例
	 */
	private final Map<String, Decoder<? extends SimpleMessage>> decoders = new HashMap<>();

	private volatile static DecoderManager instance;

	public static DecoderManager newInstance() {
		if (instance == null) {
			synchronized (DecoderManager.class) {
				if (instance == null)
					instance = new DecoderManager();
			}
		}
		return instance;
	}

	private DecoderManager() {
		initDecoder();
	}

	private void initDecoder() {
		logger.info("initializing message decoders...");

		Map<String, DecoderConfiguration> confMap = GsonUtil.toMap(ConfigurationUtil.locateFromClasspath("decoder.json"), String.class, DecoderConfiguration.class);

		if (confMap != null && confMap.size() > 0) {
			for (String key : confMap.keySet()) {
				DecoderConfiguration conf = confMap.get(key);

				Decoder<? extends SimpleMessage> decoder = newDecoder(conf);

				if (decoder != null) {
					decoders.put(key, decoder);

					logger.info("Decoder '{}' is initialized with '{}'.", key, conf.getDecoder());
				}
			}
		}
		logger.info("initializing message decoders...OK");
	}

	/**
	 * 获取指定解码器实例
	 * 
	 * @param id
	 *            解码器id，对应消息类型
	 * @return 解码器设备id
	 */
	public Decoder<? extends SimpleMessage> decoder(String id) {
		if (!decoders.containsKey(id)) {
			logger.info("try to get an non-exist decoder with id '{}'.", id);
			return null;
		}
		return decoders.get(id);
	}

	private static Decoder<? extends SimpleMessage> newDecoder(DecoderConfiguration conf) {
		Decoder<? extends SimpleMessage> decoder = null;

		if (conf != null && StringUtil.isNotBlank(conf.getDecoder())) {

			try {
				decoder = (Decoder<?>) Class.forName(conf.getDecoder()).getConstructor(List.class).newInstance(conf.getMappers());

			} catch (Exception e) {
				throw new UncheckedException("failed to create decoder for \"{0}\".", e, conf.getDecoder());
			}
		}

		return decoder;
	}
}
